using UnityEngine;
using System.Collections;

public class SwitchControl : MonoBehaviour, ICommand, IReceiver
{
    #region Attributes

    private const int FLAG_DOOR_OPEN_END = 1 << 0;
    private const int FLAG_DOOR_CLOSE_END = 1 << 1;
    private const int FLAG_DOOR_SWITCH_END = 1 << 2;
    private const int FLAG_ACTION_END_CONDITION = FLAG_DOOR_SWITCH_END | FLAG_DOOR_CLOSE_END | FLAG_DOOR_OPEN_END;

    public bool isPressed = false;
    public float waitTime = 1.5f;
    public DoorControl[] doorsToOpen;
    public DoorControl[] doorsToClose;
    public DoorControl[] doorsToSwitch;
    public Transform overviewPoint;

    private bool isReady;
    private int actionStatusMask;
    private CameraControl cameraControl;
    private IReceiver receiver;

    #endregion

    #region Methods

    private void OnStartAction()
    {
        StartCoroutine(OnAction(this.receiver));
    }

    private void OnEndAction(bool withError = false)
    {
        if (withError)
            this.isPressed = !this.isPressed;
        this.isReady = true;
        GameControl.isInputEnabled = true;
        this.receiver.Action();
//		if(isPressed)
//			//transform.Find("Switch").gameObject.renderer.materials[1].color = new Color(0,32,0);
//			transform.Find("Switch").gameObject.renderer.materials[1].SetColor("_Color", new Color(0,0,0));
//		
//		else
//			transform.Find("Switch").gameObject.renderer.materials[1].SetColor("_Color", new Color(32,32,32));
    }

    private IEnumerator OnAction(IReceiver receiver)
    {
        /* Start Tasks */
        this.actionStatusMask = 0;
        StartCoroutine(OnActionOpenDoors());
        StartCoroutine(OnActionCloseDoors());
        StartCoroutine(OnActionSwitchDoors());
        /* Wait End */
        yield return WaitEndOnAction();
        yield return new WaitForSeconds(this.waitTime);
        /* Notify */
        this.OnEndAction();
        if (this.overviewPoint)
            this.cameraControl.ChangeCameraStateTo(StateConstants.CAMERA_FOLLOW_STATE);
    }

    private IEnumerator OnActionOpenDoors()
    {
        foreach (DoorControl doorControl in this.doorsToOpen)
        {
            if (this.isPressed)
                doorControl.Open();
            else
                doorControl.Close();

            yield return new WaitForFixedUpdate();
        }

        this.actionStatusMask |= SwitchControl.FLAG_DOOR_OPEN_END;
    }

    private IEnumerator OnActionCloseDoors()
    {
        foreach (DoorControl doorControl in this.doorsToClose)
        {
            if (this.isPressed)
                doorControl.Close();
            else
                doorControl.Open();

            yield return new WaitForFixedUpdate();
        }

        this.actionStatusMask |= SwitchControl.FLAG_DOOR_CLOSE_END;
    }

    private IEnumerator OnActionSwitchDoors()
    {
        foreach (DoorControl doorControl in this.doorsToSwitch)
        {
            doorControl.Switch();
            yield return new WaitForFixedUpdate();
        }

        this.actionStatusMask |= SwitchControl.FLAG_DOOR_SWITCH_END;
    }

    private IEnumerator WaitEndOnAction()
    {
        while (this.actionStatusMask != SwitchControl.FLAG_ACTION_END_CONDITION)
        {
            yield return new WaitForFixedUpdate();
        }
    }

    #endregion

    #region Unity Methods

    void Start () 
    {
        this.isReady = true;
        if (this.overviewPoint)
            this.cameraControl = GameControl.FindCamera();
	}

    #endregion

    #region ICommand

    public void Execute(IReceiver receiver, params object[] args)
    { 
        if (this.isReady)
        {
            this.isReady = false;
            this.isPressed = !this.isPressed;
            this.receiver = receiver;
            GameControl.isInputEnabled = false;
            if (this.overviewPoint)
                this.cameraControl.Execute(this, this.overviewPoint);
            else
                this.OnStartAction();
        }
    }

    #endregion

    #region IReceiver

    public void Action(params object[] args)
    {
        try
        {
            this.OnStartAction();
        }
        catch (System.Exception e)
        {
            Debug.LogError(e.StackTrace);
            this.OnEndAction(true); // Prevent game crash on error
        }
    }

    #endregion
}
